/*
 * Java Basics Practical Tasks module1
 * 1. Prime Numbers task
 */
package module1;

import java.util.*;

/**
 *
 * @author Antonina Cherednichenko
 */
public abstract class PrimeNumbers {
    protected final int LOWER_LIMIT = 2;

    /**
     * Generating list of prime numbers, using or Sieve of Eratosthenes algorithm
     * or simple decision, depends on flag obviousDecision
     * @return - list of prime numbers
     */
    abstract ArrayList<Integer> generatePrimeNumbers(int upperLimit);

    private int getValidInput(Scanner in) throws InputMismatchException {
        System.out.println("Enter upper limit for generated prime numbers");
        int upperLimit = in.nextInt(); //can throw the exception
        if (upperLimit < LOWER_LIMIT) throw new InputMismatchException();
        return upperLimit;
    }

    private void printResults(ArrayList<Integer> primeNumbers, int upperLimit) {
        if (primeNumbers.isEmpty()) {
            System.out.printf("No prime numbers in the range [%d .. %d]\n", LOWER_LIMIT, upperLimit);
        } else {
            System.out.printf("All prime numbers in the range [%d .. %d]:\n", LOWER_LIMIT, upperLimit);
            for (int num : primeNumbers) {
                System.out.print(num + " ");
            }
            System.out.println();
        }
    }

    public void testFunction() {
        Scanner in = new Scanner(System.in);
        try {
          int upperLimit = getValidInput(in);
          ArrayList<Integer> primeNumbers = generatePrimeNumbers(upperLimit);
          printResults(primeNumbers, upperLimit);
        } catch (InputMismatchException exc) {
           System.out.println("Wrong input, expected number greater than " + LOWER_LIMIT);
        } finally {
            //in.close();
        }
    }
}

class PrimeNumbersSieveDecision extends PrimeNumbers {
    public PrimeNumbersSieveDecision() {
        System.out.println("Testing prime numbers generation via Sieve of Eratosthenes decision");
    }

   /**
     * Sieve of Eratosthenes algorithm is used for creation of prime number list
     * @return - list of prime numbers from [LOW_LIMIT.. upperLimit]
     */
     ArrayList<Integer> generatePrimeNumbers(int upperLimit) {
	boolean[] primes = new boolean[upperLimit + 1];
        //filling array with true values, except elements with 0 and 1 indexes
	Arrays.fill(primes, LOWER_LIMIT, upperLimit + 1, true);
	for (int i = LOWER_LIMIT; i * i <= upperLimit; i++) {
		if (primes[i]) {
			for (int k = i * i; k <= upperLimit; k += i) {
				primes[k] = false;
			}
		}
	}
        ArrayList<Integer> primeNumbers = new ArrayList<Integer>();
        for (int i = LOWER_LIMIT; i <= upperLimit; i++) {
            if (primes[i] == true) {
                primeNumbers.add(i);
            }
        }
	return primeNumbers;
    }
}

class PrimeNumbersObviousDecision extends PrimeNumbers {
    public PrimeNumbersObviousDecision() {
        System.out.println("Testing prime numbers generation via Obvious decision");
    }

     /**
     * Obvious decision for task
     * Written because of Implementation Requirements:
          The program should use next two nested loops and the modulus operator (%) to detect prime numbers ....
     * @return - list of prime numbers from [LOW_LIMIT.. upperLimit]
     */
    ArrayList<Integer> generatePrimeNumbers(int upperLimit) {
        ArrayList<Integer> primeNumbers = new ArrayList<Integer>();
            for (int i = LOWER_LIMIT; i <= upperLimit; i++) {
                if (isPrimeNumber(i) == true) {
                   primeNumbers.add(i);
                }
            }
        return primeNumbers;
    }

    /**
     * Cheking if number is prime.
     * Is called by getObviousDecision function
     */
     private boolean isPrimeNumber(int num) {
        for (int i = LOWER_LIMIT; i <= num / 2; i++) {
            if (num % i == 0) {
                return false;
            }
        }
        return true;
    }
}
